package org.easy.excel.support.parse.impl;

import org.easy.excel.support.parse.AbstractAnnotationExcelSourceParse;
import org.easy.excel.support.parse.source.ExcelFormatSource;
import org.easy.excel.support.parse.source.ExportLabelSource;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.tool.collection.CollectionUtil;
import org.tool.core.lang.date.DateUtil;
import org.tool.reflect.JavaBeanUtil;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import static cn.hutool.core.text.CharSequenceUtil.EMPTY;
import static org.easy.excel.configure.ParseThreadPoolConfigure.ThreadConstant.CPU_SIZE;

/**
 * Excel数据源解析之异步注解解析器
 *
 * @author 李坤
 * @date 2022/4/24 23:41
 */
@Service
public class AsyncAnnotationExcelSourceParse extends AbstractAnnotationExcelSourceParse implements ApplicationRunner {

    @Resource
    private ApplicationContext applicationContext;

    private AsyncAnnotationExcelSourceParse asyncAnnotationExcelSourceParse;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        asyncAnnotationExcelSourceParse = applicationContext.getBean(AsyncAnnotationExcelSourceParse.class);
    }

    @Override
    public List<List<String>> parseExportDataList(List<?> rawDataCollection) throws Exception {
        final Class<?> clazz = rawDataCollection.get(0).getClass();

        // 是否处理合计行
        final boolean appendTotalRow = appendTotalRow(clazz);
        // 类-字段 定义的元注解数据
        final List<ExportLabelSource> exportLabelSources = getAnnotationExcelSourceList(clazz);
        // 导出数据的总行数
        final int size = appendTotalRow ? rawDataCollection.size() + 1 : rawDataCollection.size();
        // Excel 最后的合计行结果集合
        final Map<String, BigDecimal> title2TotalRowMap = appendTotalRow ? new ConcurrentHashMap<>() : null;

        // 需要解析字段列的个数
        final int columnSize = exportLabelSources.size();
        // 解析后的导出数据
        final List<List<String>> parsedDataCollection = new ArrayList<>(size);

        int taskSize = size / (CPU_SIZE - 1);
        final AtomicInteger atomic = new AtomicInteger(0);
        taskSize = (size % (CPU_SIZE - 1) == 0) ? taskSize : taskSize++;
        final List<Future<List<List<String>>>> futureTasks = new ArrayList<>();

        CollectionUtil.segmentCollection(rawDataCollection, taskSize).forEach(rowDataSubCollection -> {
            final int index = atomic.addAndGet(rowDataSubCollection.size());
            futureTasks.add(asyncAnnotationExcelSourceParse.asyncParseGivenDate(index,
                    rowDataSubCollection, exportLabelSources, null));
        });

        for (Future<List<List<String>>> futureTask : futureTasks) {
            parsedDataCollection.addAll(futureTask.get());
        }

        if (appendTotalRow) {
            final List<Object> lastInnerList = new ArrayList<>(columnSize);

            for (ExportLabelSource exportLabelSource : exportLabelSources) {
                // 如果该列数据为 序列号 ，则其值将为该行的行号
                if (Objects.equals(exportLabelSource.getField(), EMPTY)) {
                    lastInnerList.add("合计：");
                } else {
                    BigDecimal totalRow = title2TotalRowMap.get(exportLabelSource.getField());
                    lastInnerList.add(Objects.isNull(totalRow) ? EMPTY : totalRow.toString());
                }
            }
        }
        return parsedDataCollection;
    }

    @Async("asyncExecutor")
    public Future<List<List<String>>> asyncParseGivenDate(final int atomic, final List<?> rawDataCollection,
                                                          final List<ExportLabelSource> exportLabelSources,
                                                          final Map<String, AtomicReference<BigDecimal>> title2TotalRowMap) {
        final int size = rawDataCollection.size();
        // 每条数据需要解析的字段个数
        final int columnSize = exportLabelSources.size();
        // 给定原始数据最终解析的结果
        final List<List<String>> parsedDataCollection = new ArrayList<>(size);

        List<String> innerList;
        // 遍历格式化处理集合中所有的数据
        for (int index = 0; index < size; index++) {
            parsedDataCollection.add(innerList = new ArrayList<>(columnSize));

            for (ExportLabelSource exportLabelSource : exportLabelSources) {
                // 如果该列数据为 序列号 ，则其值将为该行的行号
                if (Objects.equals(exportLabelSource.getField(), EMPTY)) {
                    innerList.add(String.valueOf(index + atomic));
                } else {
                    innerList.add(parseGivenDate4Async(rawDataCollection.get(index), exportLabelSource, title2TotalRowMap));
                }
            }
        }
        return new AsyncResult<>(parsedDataCollection);
    }

    /**
     * 解析导出的数据，根据其给定的格式，将其转换为字符串
     *
     * @param source            源数据
     * @param exportLabelSource 字段注解
     * @param title2TotalRowMap 汇总数据
     *
     * @return 解析后的字符串
     */
    protected String parseGivenDate4Async(Object source, ExportLabelSource exportLabelSource,
                                          Map<String, AtomicReference<BigDecimal>> title2TotalRowMap) {
        Object parseResult;
        final String field = exportLabelSource.getField();

        if (Objects.nonNull(parseResult = JavaBeanUtil.getBeanProperty(field, source))) {
            final Class<?> returnType = exportLabelSource.getReturnType();
            final ExcelFormatSource excelFormatSource = exportLabelSource.getExcelFormatSource();

            if (returnType.equals(BigDecimal.class)) {
                final BigDecimal result = (BigDecimal) parseResult;
                final BigDecimal decimalUnit = excelFormatSource.getDecimalUnit();
                parseResult = (result).divide(decimalUnit, excelFormatSource.getDecimal(), excelFormatSource.getRoundingMode());

                // 计算该列数据的合计值
                if (Objects.nonNull(title2TotalRowMap)) {
                    final AtomicReference<BigDecimal> reference;

                    if (Objects.nonNull(reference = title2TotalRowMap.get(field))) {
                        reference.updateAndGet(data -> data.add(result));
                    } else {
                        synchronized (field) {
                            if (Objects.isNull(reference)) {
                                title2TotalRowMap.put(field, new AtomicReference<>(result));
                            } else {
                                reference.updateAndGet(data -> data.add(result));
                            }
                        }
                    }
                }
            } else if (returnType.equals(Date.class)) {
                final LocalDateTime localDateTime = DateUtil.date2LocalDateTime((Date) parseResult);
                return localDateTime.format(getSimpleDateFormat(excelFormatSource));
            }
            return String.valueOf(parseResult);
        }
        return EMPTY;
    }

}
